package truerefactor.parsers.cpp;

import java.util.Hashtable;

/**
 * Manages the symbol table and scopes within a given compilation unit.
 */
public class SymtabManager {
    /**
     * Global symbol table indexed by the name of the scope (class/function).
     */
    static Hashtable scopeTable = new Hashtable();

    /**
     * Stack of scopes. Currently max. nesting allowed is 100.
     */
    static Scope[] scopeStack = new Scope[100];

    /**
     * Current depth of scope nesting.
     */
    static int depth = 0;

    /**
     * Dummy at the bottom of the stack so that no need to check for null.
     */
    static {
        scopeStack[depth] = new Scope(null);
    }

    /**
     * Opens a new scope (with optional name and type flag).
     */
    public static Scope OpenScope(String scopeName, boolean isType) {
        Scope newScope;

        if (scopeName != null) {
            if (isType) {
                newScope = new ClassScope(scopeName, scopeStack[depth]);
                scopeStack[depth].PutTypeName(scopeName, newScope);
            } else {
                newScope = new Scope(scopeName, isType, scopeStack[depth]);
            }

            scopeTable.put(scopeName, newScope);
        } else
            newScope = new Scope(scopeStack[depth]);

        scopeStack[++depth] = newScope;
        return newScope;
    }

    public static void OpenScope(Scope sc) {
        scopeStack[++depth] = sc;
    }

    public static void PutTypeName(String name) {
        scopeStack[depth].PutTypeName(name);
    }

    public static boolean IsFullyScopedTypeName(String name) {
        if (name == null)
            return false;

        if (name.indexOf("::") == -1)
            return IsTypeName(name);

        Scope sc = GetScopeOfFullyScopedName(name);

        if (sc != null)
            return sc.IsTypeName(name.substring(name.lastIndexOf("::") + 2,
                    name.length()));

        return false;
    }

    public static boolean IsTypeName(String name) {
        int i = depth;

        while (i >= 0) {
            if (scopeStack[i--].IsTypeName(name))
                return true;
        }

        return false;
    }

    public static void CloseScope() {
        depth--;
    }

    /**
     * For now, we just say if it is a class name, it is OK to call it a
     * constructor.
     */
    public static boolean IsCtor(String name) {
        if (name == null)
            return false;

        if (name.indexOf("::") == -1)
            return GetScope(name) != null;

        Scope sc = GetScopeOfFullyScopedName(name);

        if (sc != null && sc.parent != null)
            return sc.parent.GetScope(name.substring(name.lastIndexOf("::") + 2,
                    name.length())) == sc;

        return false;
    }

    public static Scope GetCurScope() {
        return scopeStack[depth];
    }

    public static Scope GetScope(String name) {
        int i = depth;
        Scope sc = null;

        while (i >= 0)
            if ((sc = scopeStack[i--].GetScope(name)) != null)
                return sc;

        return null;
    }

    /**
     * Returns the Scope of B in A::B::C.
     */
    public static Scope GetScopeOfFullyScopedName(String name) {
        Scope sc;
        int i = 0, j = 0;

        if (name.indexOf("::") == -1)
            return GetScope(name);

        if (name.indexOf("::") == 0) {
            sc = scopeStack[1];
            j = 2;
        } else
            sc = GetCurScope();

        String tmp = name.substring(j, name.lastIndexOf("::"));

        while ((j = tmp.indexOf("::", i)) != -1) {
            sc = sc.GetScope(tmp.substring(i, j));
            i = j + 2;

            if (sc == null)
                return null;
        }

        if (sc == GetCurScope())
            return GetScope(tmp.substring(i, tmp.length()));

        return sc.GetScope(tmp.substring(i, tmp.length()));
    }

    public static boolean IsGlobalScope() {
        return depth == 1 || depth == 2;
    }
}
